package upload

import (
	"context"
	"errors"
	"fmt"
	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"
	"github.com/spf13/viper"
	"io"
	"strings"
	"sync"
)

var bucketName = "cgs"

var minioClient *minio.Client
var dbOnce sync.Once

func GetMinIOClient() (*minio.Client, error) {
	endpoint := viper.GetString("minio.endpoint")
	accessKeyID := viper.GetString("minio.access-key-id")
	secretAccessKey := viper.GetString("minio.secret-access-key")
	useSSL := viper.GetBool("minio.useSSL")
	bktName := viper.GetString("minio.bucketName")
	if bktName != "" {
		bucketName = bktName
	}
	if endpoint == "" || accessKeyID == "" || secretAccessKey == "" {
		return nil, errors.New("minio configuration is required")
	}

	var err error
	if minioClient == nil {
		dbOnce.Do(func() {
			// Initialize minio client object.
			minioClient, err = minio.New(endpoint, &minio.Options{
				Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
				Secure: useSSL,
			})
		})
	}

	return minioClient, err
}

type MinIOStore struct {
	client *minio.Client
}

func (m *MinIOStore) makeBucket(bucketName string) error {
	var policy = `{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "s3:GetObject"
      ],
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "*"
        ]
      },
      "Resource": [
        "arn:aws:s3:::` + bucketName + `/*"
      ],
      "Sid": ""
    }
  ]
}`
	if m.client == nil {
		return errors.New("minio client create failed")
	}
	exists, err := m.client.BucketExists(context.Background(), bucketName)
	if err != nil {
		return err
	}
	if !exists {
		err := m.client.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{
			Region:        "zh",
			ObjectLocking: false,
		})
		if err != nil {
			return err
		}
		err = m.client.SetBucketPolicy(context.Background(), bucketName, policy)
		if err != nil {
			return err
		}
	}
	return nil
}

func (m *MinIOStore) Upload(file io.Reader, filename string, fileSize int64, fileType string) (string, error) {
	if err := m.makeBucket(bucketName); err != nil {
		return "", err
	}

	uploadInfo, err := m.client.PutObject(context.Background(), bucketName, filename, file, fileSize, minio.PutObjectOptions{
		ContentType: fileType,
	})
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s/%s", bucketName, uploadInfo.Key), nil
}

func (m *MinIOStore) Delete(key string) error {
	if err := m.makeBucket(bucketName); err != nil {
		return err
	}
	strings.TrimPrefix(key, bucketName+"/")
	err := m.client.RemoveObject(context.Background(), bucketName, key, minio.RemoveObjectOptions{})
	return err
}

func (m *MinIOStore) Get(key string) (io.Reader, error) {
	if err := m.makeBucket(bucketName); err != nil {
		return nil, err
	}
	strings.TrimPrefix(key, bucketName+"/")
	object, err := m.client.GetObject(context.Background(), bucketName, key, minio.GetObjectOptions{})
	return object, err
}

func newMinIOStore() FileStore {
	client, err := GetMinIOClient()
	if err != nil {
		fmt.Println(err)
		return nil
	}
	return &MinIOStore{
		client: client,
	}
}
